/*
 * @FilePath: base.ts
 * @Author: liuqiong
 * @Date: 2023-09-14 16:50:01
 * @LastEditors: wanglx
 * @LastEditTime: 2024-09-03 17:18:04
 * @Description:
 */
import axios, {
  AxiosError,
  type AxiosInstance,
  type AxiosRequestConfig,
  type AxiosResponse,
  type CreateAxiosDefaults,
  type InternalAxiosRequestConfig
} from 'axios'
import qs from 'qs'

const handleError = (error: any, defaultMsg?: string) => {
  if (!axios.isCancel(error)) {
  }
}

const createRequest = ({
  axiosConfig,
  onLogout
}: {
  axiosConfig?: CreateAxiosDefaults
  onLogout?: (err?: any) => void
}) => {
  const request = axios.create({
    timeout: 60 * 1000,
    paramsSerializer: (params) => qs.stringify(params, { indices: false }),
    withCredentials: true,
    ...axiosConfig
  })

  function handleRequest(req: InternalAxiosRequestConfig) {
    return req
  }
  function handleResponse(res: AxiosResponse) {
    const code = res.data?.code
    if (res.status === 200 && res.data instanceof Blob) {
      return res
    }
    if (!code ?? true) {
      switch (code) {
        case 40101:
        case 40103:
          handleError(res.data)
          onLogout?.(res.data)
          break
        case 200:
          return res.data.data
        case 0:
          return res.data.data
        default:
          handleError(res.data)
          return Promise.reject(res.data)
      }
    } else {
      if (res.status === 200 && res.data instanceof Blob) {
        return res
      }
      return res.data
    }
  }
  function handleRequestError(error: AxiosError) {
    handleError(error)
    return Promise.reject(error)
  }
  function handleResponseError(error: AxiosError) {
    const statusCode = error.response?.status ?? 0
    if (statusCode === 401) {
      onLogout?.()
    } else {
      handleError(error)
      return Promise.reject(error)
    }
  }
  request.interceptors.request.use(handleRequest, handleRequestError)
  request.interceptors.response.use(handleResponse, handleResponseError)
  return request
}

interface AxiosInstanceWithDefaultConfig {
  <T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>
  get<T = any, R = AxiosResponse<T>, D = any>(
    url: string,
    config?: AxiosRequestConfig<D>
  ): Promise<R>
  delete<T = any, R = AxiosResponse<T>, D = any>(
    url: string,
    config?: AxiosRequestConfig<D>
  ): Promise<R>
  head<T = any, R = AxiosResponse<T>, D = any>(
    url: string,
    config?: AxiosRequestConfig<D>
  ): Promise<R>
  options<T = any, R = AxiosResponse<T>, D = any>(
    url: string,
    config?: AxiosRequestConfig<D>
  ): Promise<R>
  post<T = any, R = AxiosResponse<T>, D = any>(
    url: string,
    data?: D,
    config?: AxiosRequestConfig<D>
  ): Promise<R>
  put<T = any, R = AxiosResponse<T>, D = any>(
    url: string,
    data?: D,
    config?: AxiosRequestConfig<D>
  ): Promise<R>
  patch<T = any, R = AxiosResponse<T>, D = any>(
    url: string,
    data?: D,
    config?: AxiosRequestConfig<D>
  ): Promise<R>
}
const requestWithDefaultConfig = (
  axiosInstance: AxiosInstance,
  defaultConfig: AxiosRequestConfig
): AxiosInstanceWithDefaultConfig => {
  const request = <T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>) =>
    axiosInstance<T, R, D>(Object.assign(defaultConfig, config))
  const instance = Object.assign(request, {
    get: <T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>) => {
      return axiosInstance.get<T, R, D>(url, Object.assign(defaultConfig, config))
    },
    delete: <T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      config?: AxiosRequestConfig<D>
    ) => {
      return axiosInstance.delete<T, R, D>(url, Object.assign(defaultConfig, config))
    },
    head: <T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>) => {
      return axiosInstance.head<T, R, D>(url, Object.assign(defaultConfig, config))
    },
    options: <T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      config?: AxiosRequestConfig<D>
    ) => {
      return axiosInstance.options<T, R, D>(url, Object.assign(defaultConfig, config))
    },
    post: <T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>
    ) => {
      return axiosInstance.post<T, R, D>(url, data, Object.assign(defaultConfig, config))
    },
    put: <T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>
    ) => {
      return axiosInstance.put<T, R, D>(url, data, Object.assign(defaultConfig, config))
    },
    patch: <T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: AxiosRequestConfig<D>
    ) => {
      return axiosInstance.patch<T, R, D>(url, data, Object.assign(defaultConfig, config))
    }
  })
  return instance
}

export { createRequest, handleError, requestWithDefaultConfig }
